/*
 * @(#)DataTxt.java 1.00 30 de julio de 2007
 *
 * Copyright (c) 2005-2007 Soluciones Info Móviles SAC.
 * Av. Javier Prado Oeste 203 Piso 13, Lima 27, Perú.
 *
 * Todos los derechos reservados.
 */
package sim.store;

import java.util.Vector;
import sim.utils.parser.*;
import sim.utils.*;
import java.io.*;
import java.util.*;

/**
 * Esta clase permite la construcción de arreglos de cadenas de caracteres
 * para su acceso o modificaión a través de indices.
 *
 * un objeto de este tipo permite la representación del arreglo en una cadena
 * caracteres llamada "linea de datos". En una linea de datos
 * los elementos son delimitados por un caracter conocido como token.
 *
 * Las variables de este tipo solo pueden ser utilizadas por las funciones
 * de este modulo.
 *
 */
public final class DataTxt implements ParserScan {

    private static final int SIZE_INIT_SIZE = 4;

    private static final byte[] FLD_EMPTY = "".getBytes();

    private Vector m_Fields = null;
    private String m_Encoding = null;
    private byte[] m_Token = null;
    private boolean m_Modified = false;

    // Auxiliar, para crear las lineas de datos.
    private Buffer m_Buffer = null;
    
    protected StoreTxtId m_RecordID = new StoreTxtId();
    
    /** 
     * 
     * 
     * @param token 
     * @param start 
     * @param count 
     * @param initSize 
     * @param encoding 
     * 
     * @return 
     */
    public DataTxt(final byte[] token, int start, int count, 
		   int initSize, final String encoding) {
	// Longitud del vector.
	m_Fields = new Vector((initSize < SIZE_INIT_SIZE) ? SIZE_INIT_SIZE : initSize);
	// Token
	m_Token = new byte[count];
	Utils.memcpy(m_Token, 0, token, start, count);
	// Encoding
	m_Encoding = encoding;
    }

    public DataTxt(final byte[] token, int initSize, final String encoding) {
	this(token, 0, token.length, initSize, encoding);
    }

    public DataTxt(final byte[] token, int start, int count, int initSize) {
	this(token, start, count, count, null);
    }

    public DataTxt(final byte[] token, int start, int count) {
	this(token, start, count, SIZE_INIT_SIZE, null);
    }

    public boolean modified() {
	return m_Modified;
    }

    public void setModified(boolean modified) {
	m_Modified = modified;
    }

    public int make(final byte[] record, int start, int count){
	Parser p;
        m_Fields.removeAllElements();
        if (record != null){
	    p = new Parser(this, m_Token, Parser.FORWARD);
            p.scan(record, start, count);
        }
        return m_Fields.size();
    }

    public int make(final byte[] record, int start){
        return make(record, start, record.length - start);
    }
    
    public int make(final byte[] record){
        return make(record, 0, record.length);
    }
    
    public boolean parserHit(final int hit, final byte[] line, int start, int length, Object something) {
	byte[] bytes;
	if (length > 0) {
	    bytes = new byte[length];
	    Utils.memcpy(bytes, 0, line, start, length);
	}
	else {
	    bytes =  FLD_EMPTY;
	}
	m_Fields.addElement(bytes);
        return true;
    }
    
    public boolean parserEnd(boolean cancel) {
        return true;
    }

    public void set(int indexField, byte[] line, int start, int count) {
	int i;
	byte[] bytes = null;
	i = m_Fields.size();
	while (i <= indexField) {
	    m_Fields.addElement(FLD_EMPTY);
	    i++;
	}
	bytes = new byte[count];
	Utils.memcpy(bytes, 0, line, start, count);
        m_Fields.setElementAt(bytes, indexField);
	m_Modified = true;
    }

    public void set(int indexField, byte[] line, int start) {
	set(indexField, line, 0, line.length - start);
    }

    public void set(int indexField, byte[] line) {
	set(indexField, line, 0, line.length);
    }

    public void set(int indexField, final String value) {
	set(indexField, Utils.encoding(value, m_Encoding));
    }

    public void set(int indexField, int value, int base) {
	int length;
	// Si el buffer no existe ...
	if (m_Buffer == null) {
	    // ... crearlo
	    createBuffer(16);
	}
	// Si el buffer existe ...
	if (m_Buffer != null) {
	    // ... separar espacio para representar un número entero
	    m_Buffer.appendSpace(16);
	    m_Buffer.clear();
	    // ... imprimir el número
	    length = Utils.fmtInteger(m_Buffer.bytes(), 0, 32, value,
				      base, 0, Utils.FMT_LEFT);
	    // añadir el núnero
	    set(indexField, m_Buffer.bytes(), 0, length);
	}
    }
    
    public void set(int indexField, double value) {
	set(indexField, Double.toString(value));
    }

    public final byte[] getBytes(int indexField){
        if((indexField > -1) && (indexField < m_Fields.size())) {
            return (byte[])m_Fields.elementAt(indexField);
        }
        return FLD_EMPTY;
    }

    public final String getString(int indexField){
	return Utils.decoding(getBytes(indexField), m_Encoding);
    }
    
    public final double getDouble(int indexField){
	return Utils.memtod(getBytes(indexField));
    }    
    
    public final int getInteger(int indexField, int base){
	return Utils.memtoul(getBytes(indexField), base);
    }        

    public final boolean getBoolean(int indexField) {
	return (getInteger(indexField, 10) > 0);
    }

    /**
     * Devuelve el numero de elementos del arreglo
     *
     * @return Numero de elementos del arreglo.
     */
    public int size(){
        return m_Fields.size();
    }

    public int length() {
	Enumeration e;
	byte[] bytes;
	int length = 0;
	e = m_Fields.elements();
	while(e.hasMoreElements()) {
	    bytes = (byte[])e.nextElement();
	    if (length > 0) {
		length += m_Token.length;
	    }
	    length += bytes.length;
	}
	return length;
    }
    
    /**
     * Comprueba si el N-esimo elemento del arreglo esta vacio
     *
     * @return  <true> si el elemento que se comprueba se encuentra
     *          <false> en caso contrario.
     */
    public boolean empty(int n){
	if ((n > 0) && (n < size())) {
	    return (getBytes(n).equals(FLD_EMPTY));
	}
	return true;
    }

    public final Buffer toRecord() {
	builRecord();
	return m_Buffer;
    }

    private void createBuffer(int length) {
	m_Buffer = new Buffer((length > 0) ? length : FLD_EMPTY.length);    
    }

    private void builRecord() {
	Enumeration e;
	int length;
	// Calcular la longitud del del record
	length = length();
	// Crear buffer.
	if (m_Buffer == null) {
	    createBuffer(length);
	}
	m_Buffer.clear();
	// Crear el record.
	if (length > 0) {
	    // Asegurar una capacidad por lo menos exacta.
	    m_Buffer.appendSpace(length);
	    m_Buffer.clear();
	    // Añadir al buffer todos los items separados por token
	    e = m_Fields.elements();
	    while(e.hasMoreElements()) {
		if (m_Buffer.length() > 0) {
		    m_Buffer.append(m_Token);
		}
		m_Buffer.append((byte[])e.nextElement());
	    }
	}
    }

    /**
     * FUNCIONES PARA MODIFICAR EL RECORD.
     * 
     */
    public final int getRecordOffset() {
	return m_RecordID.m_Offset;
    }

    public final void setRecordOffset(int offset) {
	m_RecordID.m_Offset = offset;
    }
}
